# -*- coding: utf-8 -*-

import os
from wsgiref.simple_server import server_version

import xlrd
import sys
import hashlib
import json
import export_rules

is_warning_open = False
md5_data = {}
tab = "    "

data_folder = "..\\excel_files"
out_folder = "..\\json_files"
csharp_folder = "..\\csharp_data\\"
md5_path = "..\\excel_files\\md5.json"

START_LINE = 4

all_errors = []

def normalize_key(keys_row, index):
    key = keys_row[index]
    if isinstance(key, float):
        key = "number_" + str(int(key))
        keys_row[index] = key
    elif isinstance(key, int):
        key = "number_" + str(key)
        keys_row[index] = key
    return key

def normalize_id(id_flag, row_data, row_index):
    if id_flag == -1:
        table_key = row_index - START_LINE + 1
    else:
        table_key = row_data[id_flag]

    return table_key

def gen_output_file_path(output_filename):
    client_path = out_folder + "\\client_data\\" + output_filename
    return  client_path

def read_md5(file_path):
    global md5_data
    try:
        with open(file_path, 'rb') as f:
            md5_data = json.load(f)
    except:
        print("No json file, now create")


def write_md5(file_path):
    with open(file_path, 'w') as f:
        json.dump(md5_data, f)


def calc_md5(filepath):
    with open(filepath,'rb') as f:
        md5obj = hashlib.md5()
        md5obj.update(f.read())
        hash_data = md5obj.hexdigest()
        return hash_data


# 计算row_index对应的excel列名称，row_index从1开始
def calc_excel_row(row_index):
    if row_index < 1:
        print("Error in calc_excel_row, wrong row index: ", row_index)

    # 将bytearray改为list来存储字符
    row_chars = []
    itr = row_index
    while itr != 0:
        last_chr = chr((itr - 1) % 26 + ord('A'))
        row_chars.append(last_chr)
        itr = int((itr - 1) / 26)

    # 反转字符列表
    row_chars.reverse()
    
    # 将字符列表连接成字符串
    return ''.join(row_chars)


def error_location(line, row, err_msg, table_name=""):
    err_msg_with_location = ""
    if row:
        row_str = calc_excel_row(row)
        #print("\nError in line " + str(line) + " row " + row_str + ":", end=' ')
        err_msg_with_location = table_name + ": Error in line " + str(line) + " row " + row_str + ":" + err_msg
    else:
        #print("\nError in line " + str(line) + ":", end=' ')
        err_msg_with_location = table_name + ": Error in line " + str(line) + ":" + err_msg
    print(err_msg_with_location)
    all_errors.append(err_msg_with_location)


def warning_location(line, row, err_msg):
    if not is_warning_open:
        return

    if row:
        row_str = calc_excel_row(row)
        print("\nWarning in line " + str(line) + " row " + row_str + ":", end=' ')
    else:
        print("\nWarning in line " + str(line) + ":", end=' ')
    print(err_msg)
        

def safe_data_convert(data, type_name):
    is_match = True
    err_msg = ""
    new_data = None

    if type_name == "int":
        if data == "":
            data = 0
        try:
            new_data = str(int(data))
        except:
            is_match = False
            err_msg = "非int型数据: " + data
    elif type_name == "float":
        if data == "":
            data = 0
        try:
            new_data = str(float(data))
        except:
            is_match = False
            err_msg = "非float型数据: " + data
    elif type_name == "bool":
        if data == "":
            data = "false"
        try:
            new_data = str(bool(data)).lower()
        except:
            is_match = False
            err_msg = "非bool型数据: " + data
    elif type_name == "string":
        try:
            if type(data) == type(1.0):
                if int(data) == data:
                    data = int(data)
        
            data = str(data)
            new_data = data.replace('"', '\\"')
            new_data = "\"" + new_data + "\""
        except:
            is_match = False
            err_msg = "非string型数据: " + data
    elif type_name == "list_int":
        if data == "":
            new_data = "["
        elif isinstance(data, str):
            data = data.replace('=', '|')
            split_str = data.split('|')
            for index, item in enumerate(split_str):
                try:
                    split_str[index] = str(int(item))
                    if new_data is None:
                        new_data = "["
                    new_data += split_str[index]
                    new_data += ", "
                except:
                    is_match = False
                    err_msg = "list_int必须为'|'隔开的整数: " + data
                    break
            try:
                new_data = new_data[:-2]
            except:
                is_match = False
                err_msg = "list_int必须为'|'隔开的整数: " + data
        else:
            new_data = "["
            try:
                new_data += str(int(data))
            except:
                is_match = False
                err_msg = "list_int:非int型数据: " + data
        new_data += "]"
    elif type_name == "list_float":
        if data == "":
            new_data = "["
        elif isinstance(data, str):
            data = data.replace('=', '|')
            split_str = data.split('|')
            for index, item in enumerate(split_str):
                try:
                    split_str[index] = str(float(item))
                    if new_data is None:
                        new_data = "["
                    new_data += split_str[index]
                    new_data += ", "
                except:
                    is_match = False
                    err_msg = "list_int必须为'|'隔开的浮点数: " + data
                    break
            try:
                new_data = new_data[:-2]
            except:
                is_match = False
                err_msg = "list_int必须为'|'隔开的浮点数: " + data
        else:
            new_data = "["
            try:
                new_data += str(float(data))
            except:
                is_match = False
                err_msg = "list_float:非float型数据: " + data
        new_data += "]"
    else:
        is_match = False
        err_msg = "错误的数据类型: " + type_name

    return new_data, is_match, err_msg


def read_excel_data(filename):
    print("\n\n开始处理表: ", filename)
    print("处理读取页中")
    game_data = {}
    excel_data = xlrd.open_workbook(filename)
    # 第1页映射了中文sheet名称和对应英文table名称
    table0 = excel_data.sheets()[0]

    # 读取第一页中的导航页中的所有字段，每个字段对应excel中的一张子表
    result = 0
    for table_index in range(table0.nrows):
        tablename_cn, tablename_en = table0.row_values(table_index)[0:2]
        if len(tablename_cn) == 0 or len(tablename_en) == 0:
            continue
        
        tablename_en = tablename_en.strip()
        try:
            table = excel_data.sheet_by_name(tablename_cn)
        except:
            #print u"读取sheet出错：" + tablename_cn
            error_location(table_index + 1, None, "读取sheet出错：" + tablename_cn, tablename_en)
            result = -1
            continue

        game_data[tablename_en] = table

    if result == -1:
        return -1
    return game_data


def normal_cs_load_file(out_filename, tablename_en, table):
    file_str = "using System;\n"
    file_str += "using System.Collections.Generic;\n"
    file_str += "using Core;\n\n"
    file_str += "namespace g_survivors\n{\n"


    print("开始处理sheet to csharp: ", tablename_en)
    #file_str += tab + "\"" + tablename_en + "\" : {\n"
    # 第一行是注释，第二行是对应的拼音字段, 第三行是类型
    try:
        desc_row = table.row_values(0)
        keys_row = table.row_values(1)
        type_row = table.row_values(2)
        cs_row = table.row_values(3)
    except:
        err_msg = "第一行是注释，第二行是对应的拼音字段, 第三行是类型"
        error_location(1, None, err_msg, tablename_en)
        return -1

    key_name = keys_row[0]
    key_type = type_row[0]

    # class_name = tablename_en + '_JsonData'
    file_str += tab + "[Serializable]\n"
    file_str += tab + "public partial class " + tablename_en + "_JsonData : IJsonTableData<"+key_type+">\n"
    file_str += tab + "{\n"
    file_str += tab * 2 + "public "+key_type+" Index => " + key_name + ";\n"

    for index in range(len(keys_row)):
        try:
            key = normalize_key(keys_row, index)
            cs_flag = cs_row[index]
            if cs_flag == "s":
                continue
            #     server_lines.append(tab * 3 + "\"" + str(key) + "\" : " + item + ",")
            # if cs_flag == "c" or cs_flag == "cs":
            #     client_lines.append(tab * 3 + "\"" + str(key) + "\" : " + item + ",")

            type_row[index] = type_row[index].strip()
            if len(key) != 0:
                if str(type_row[index]) not in ["int", "float", "bool", "string", "list_int", "list_float"]:
                    err_msg = "类型必须为int, float, bool ,string, list_int, list_float之一 " + str(key) + ": " + str(
                        type_row[index]) + tab + str(desc_row[index])
                    error_location(3, index + 1, err_msg, tablename_en)
                    return -1
                file_str += "\n"
                file_str += tab * 2 + "/// <summary>\n"
                file_str += tab * 2 + "/// " + str(desc_row[index]).replace("\n","").replace("\r","") + "\n"
                file_str += tab * 2 + "/// </summary>\n"

                if type_row[index] == "int":
                    file_str += tab * 2 + "public int " + key + ";\n"
                elif type_row[index] == "float":
                    file_str += tab * 2 + "public float " + key + ";\n"
                elif type_row[index] == "bool":
                    file_str += tab * 2 + "public bool " + key + ";\n"
                elif type_row[index] == "string":
                    file_str += tab * 2 + "public string " + key + ";\n"
                elif type_row[index] == "list_int":
                    file_str += tab * 2 + "public List<int> " + key + ";\n"
                elif type_row[index] == "list_float":
                    file_str += tab * 2 + "public List<float> " + key + ";\n"
                else:
                    err_msg = "类型必须为int, float, bool ,string, list_int, list_float之一 " + str(
                        key) + ": " + str(type_row[index]) + tab + str(desc_row[index])
                    error_location(3, index + 1, err_msg, tablename_en)
                    return -1

                #file_str += tab * 2 + str(key) + ": " + str(type_row[index]) + tab + desc_row[index] + "\n"
        except:
            err_msg = "类型必须为int, float, bool ,string, list_int, list_float之一 " + str(key) + ": " + \
                      str(type_row[index]) + tab + str(desc_row[index])
            error_location(3, index + 1, err_msg, tablename_en)
            return -1
    
    file_str += tab + "}\n\n"


    file_str += tab + "[Serializable]\n"
    file_str += tab + "public partial class " + tablename_en + "_JsonTable : AbstractJsonTable<" + key_type + ", " + tablename_en + "_JsonData>\n"
    file_str += tab + "{\n"
    file_str += tab * 2 + "public override string TableName => \"" + out_filename + "\";\n"
    file_str += tab * 2 + "public override string SheetName => \"" + tablename_en + "\";\n"

    file_str += tab + "}\n"
    file_str += "}\n"

    return file_str

def normal_json_load_file(tablename_en, table):
    prefix = tab + "\"list\": [\n"
    # 第一行是注释，第二行是对应的拼音字段, 第三行是类型, 第四行是导出空间(c是客户端,s是服务器,cs是客户端和服务器)
    try:
        desc_row = table.row_values(0)
        keys_row = table.row_values(1)
        type_row = table.row_values(2)
        cs_row = table.row_values(3)
    except:
        err_msg = "第一行是注释，第二行是对应的拼音字段, 第三行是类型, 第四行是导出空间(c是客户端,s是服务器,cs是客户端和服务器)"
        error_location(1, None, err_msg, tablename_en)
        return -1, "", ""

    for index in range(len(keys_row)):
        try:
            type_row[index] = type_row[index].strip()
            key = normalize_key(keys_row, index)

            if len(key) != 0:
                key_table = key.split('|')
                if len(key_table) > 1 :
                    keys_row[index] = key_table[1]
                else:
                    keys_row[index] = key_table[0]

                type = str(type_row[index]).split('|')[0]
                if type not in ["int", "float", "bool", "string", "list_int", "list_float"]:
                    err_msg = "类型必须为int, float, bool ,string, list_int, list_float之一 " + str(keys_row[index]) + ": " + str(
                        type_row[index]) + tab + str(desc_row[index])
                    error_location(3, index + 1, err_msg, tablename_en)
                    return -1, "", ""
        except:
            err_msg = "类型必须为int, float, bool ,string, list_int, list_float之一 " + str(keys_row[index]) + ": " + str(
                type_row[index]) + tab + str(desc_row[index])
            error_location(3, index + 1, err_msg, tablename_en)
            return -1, "", ""

    id_flag = -1
    for i in range(len(keys_row)):
        if "ID" == str(keys_row[i]):
            id_flag = i

    #lines = []
    client_lines = []
    index = 0

    result = 0
    for row_index in range(START_LINE, table.nrows):
        row_data = table.row_values(row_index)
        table_key = normalize_id(id_flag, row_data, row_index)

        if isinstance(table_key, str):
            table_key = "\"" + table_key + "\""
        else:
            table_key, is_match, err_msg = safe_data_convert(table_key, "int")
            table_key = "\"" + table_key + "\""
            if not is_match:
                error_location(row_index + 1, index + 1, err_msg, tablename_en)
                result = -1
                continue

        first_key = row_data[0]
        if table_key == "\"\"" or table_key == u"\"\"" or first_key == "":
            err_msg = "索引值或第一列为空，略过本行"
            warning_location(row_index + 1, index + 1, err_msg)
            continue

        #client_lines.append(tab * 2 + str(table_key) + " : {")
        client_lines.append(tab * 2  + "{")

        length = len(row_data)
        for index in range(length):
            key = keys_row[index]
            if len(key) == 0:
                continue
            typee = type_row[index]
            type_table = typee.split('|')
            type = type_table[0]
            if len(type_table) > 1 and type_table[1] == 's':
                continue
            item = row_data[index]

            item, is_match, err_msg = safe_data_convert(item, type)
            if not is_match:
                error_location(row_index + 1, index + 1, err_msg, tablename_en)
                result = -1
                continue

            cs_flag = cs_row[index]
            if cs_flag == "c" or cs_flag == "cs":
                client_lines.append(tab * 3 + "\"" + str(key) + "\": " + item + ",")

        client_lines[-1] = client_lines[-1][:-1]  # 移除最后一个逗号
        client_lines.append(tab * 2 + "},")

    if client_lines:  # 如果有数据
        client_lines[-1] = client_lines[-1][:-1]  # 移除最后一个逗号

    client_content = prefix + "\n".join(client_lines) + "\n" + tab + "],\n"
    return result, client_content

def write_to_json(data, out_filename, in_filename=""):
    #file_name = '..\\json_files\\' + out_filename
    # client_file_path = gen_output_file_path(out_filename)
    client_file_path = out_folder + "\\client_data\\" 

    result = 0
    for tablename_en, table in data.items():
        print("开始处理sheet to json: ", tablename_en)
        ret, client_table_str = normal_json_load_file(tablename_en, table)
        if ret == -1:
            result = -1
            continue
        
        client_content = "{ \n"
        client_content += client_table_str
        client_content = client_content[:-2] + "\n"
        client_content += "}"

        with open(client_file_path + "" + tablename_en + ".json", "w+", encoding='utf-8') as fout:
            fout.write(client_content)
            
        csharp_content = normal_cs_load_file(out_filename, tablename_en, table)
        if csharp_content == -1:
            result = -1
            print("生成csharp文件失败", tablename_en)
            continue
        csharp_file_path = csharp_folder + tablename_en + ".cs"
        with open(csharp_file_path, "w+", encoding='utf-8') as fout:
            fout.write(csharp_content)

    return result


def convert_excel_to_json(in_filename, out_filename):
    if in_filename.split(".")[-1] != "xlsx":
        return

    data = read_excel_data(in_filename)
    if data == -1:
        return -1

    #write_to_json(data, out_filename, in_filename)
    rst = write_to_json(data, out_filename, in_filename)
    if rst == -1:
        return -1


def process_file(file_name, out_filename):
    file_path = data_folder + "\\" + file_name
    if not os.path.exists(file_path):
        print(file_path + "不存在")
    
    file_md5 = calc_md5(file_path)

    #如果md5没变且相应lua文件存在则不处理
    if file_name in md5_data.keys():
        if md5_data[file_name] == file_md5:
            client_file_path = gen_output_file_path(out_filename)
            if os.path.isfile(client_file_path):
                return
    md5_data[file_name] = file_md5

    rst = convert_excel_to_json(file_path, out_filename)
    if rst == -1:
        del md5_data[file_name]


def list_dir():
    """遍历所有的文件进行相关处理"""
    name_map = export_rules.name_map
    for file_name in name_map.keys():
        #out_file_path = out_folder + "\\" + export_rules.name_map[file_name] + ".json"
        out_file_name = export_rules.name_map[file_name]
        process_file(file_name, out_file_name)


if __name__ == "__main__":
    read_md5(md5_path)
    list_dir()

    if all_errors:
        # 处理失败
        print("\n\n处理失败，错误信息如下：")
        for err in all_errors:
            print(err)
    else:
        print("处理成功")
        write_md5(md5_path)
